package com.nuanshui.heatedloan.credit.pay;

import com.aipg.common.AipgReq;
import com.aipg.common.AipgRsp;
import com.aipg.common.XSUtil;
import com.aipg.qtd.QTDRspDetail;
import com.aipg.rnp.XMLParseExtension;
import com.aipg.transquery.QTDetail;
import com.allinpay.XmlTools;
import com.nuanshui.framework.lang.SysStringUtils;
import com.nuanshui.framework.springContext.SpringContextUtil;
import com.nuanshui.framework.utils.LogUtil;
import com.nuanshui.heatedloan.baseservice.form.StatisticsForm;
import com.nuanshui.heatedloan.constant.RabbitMqQueue;
import com.nuanshui.heatedloan.credit.facade.AllinPayTransService;
import com.nuanshui.heatedloan.credit.model.record.AllinPayReqRecord;
import com.nuanshui.heatedloan.credit.model.record.AllinPayBillRecord;
import com.nuanshui.heatedloan.middleware.mongodb.BaseRecord;
import com.nuanshui.heatedloan.credit.pay.behavior.PayBehavior;
import com.nuanshui.heatedloan.credit.pay.constant.PayBehaviorType;
import com.nuanshui.heatedloan.credit.pay.execute.PayExecuteFacade;
import com.nuanshui.heatedloan.credit.pay.utils.AllinPayUtil;
import com.nuanshui.heatedloan.credit.pay.validation.ValidationResult;
import com.nuanshui.heatedloan.credit.pay.validation.ValidationResultCode;
import com.nuanshui.heatedloan.middleware.rabbitmq.RabbitmqMessageSender;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

@Service
public class AllinPayTool {


    private static final Logger logger = LoggerFactory.getLogger(AllinPayTool.class);

    @Autowired
    private AllinPayTransService allinPayTransService;

    @Autowired
    private PayExecuteFacade payExecuteFacade;

    @Autowired
    private AllinPayAccountMgr allinPayAccountMgr;


    public PayResult executeNotify(final AllinPayResultNotify notify) {

        PayContext context=null;

        if(SysStringUtils.isNotEmpty(notify.getBATCHID()))
        {
            final AllinPayResponse response=allinPayTransService.queryTrade(notify.getBATCHID());
            Future<PayContext> result = taskExecutor.submit(new Callable<PayContext>() {
                @Override
                public PayContext call() throws Exception {
                    PayContext.removeContext();
                    if(response!=null && AllinPayResultCode.isSuccess(response.getINFO().getRET_CODE()))
                    {
                        QTDetail detail=response.getData();
                        PayContext.getContext().setTradeDetail(response);

                        PayRequest request=new PayRequest();
                        request.setOrderNo(detail.getREMARK());
                        request.setTransRequestCode(response.getINFO().getTRX_CODE());
                        PayContext.getContext().setPayRequest(request);
                        PayContext.getContext().setAllinPayResultNotify(notify);

                        PayBehavior behavior = SpringContextUtil.getBean(PayBehaviorType.PaySuccessBehavior);

                        if (behavior != null)
                            behavior.doInvoke();
                    } else
                    {
                        ValidationResult validationResult=new ValidationResult(ValidationResultCode.VALIDATION_ERROR_ALLINPAY_NOTRADEID);
                        PayContext.getContext().setValidationResult(validationResult);
                    }
                    return PayContext.getContext();
                }
            });

            try {
                context = result.get();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }

        sendStatistics(notify);

        PayResult payResult=context.fetchPayResult();

        return payResult;
    }


    /**
     * 走支付流程
     * @param request
     * @return
     */
    public PayResult execute(final PayRequest request) {

        PayContext context = null;
        Future<PayContext> result = taskExecutor.submit(new Callable<PayContext>() {
            @Override
            public PayContext call() throws Exception {
                PayContext.removeContext();
                PayContext.getContext().setPayRequest(request);
                payExecuteFacade.execute();
                return PayContext.getContext();
            }
        });

        try {
            context = result.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

        PayResult payResult = context.fetchPayResult();

        sendTradeRecord(context);
        return payResult;
    }

    @Autowired
    private ThreadPoolTaskExecutor taskExecutor;

    public <T,V> PayContext execute(final String trxCode, final Callable<V> callable)
    {
        PayContext context=null;

        if(SysStringUtils.isEmpty(trxCode))
        {
            return context;
        }

        Future<PayContext> result=taskExecutor.submit(new Callable<PayContext>() {
            @Override
            public PayContext call() throws Exception {
                PayContext.removeContext();
                AipgReq aipgReq=allinPayAccountMgr.createReq();
                PayContext.getContext().setAipgReq(aipgReq);
                aipgReq.getINFO().setTRX_CODE(trxCode);

                try {
                    aipgReq.addTrx(callable.call());
                } catch (Exception e) {
                    e.printStackTrace();
                }
                AipgRsp aipgRsp=sendReq(aipgReq);
                T rep=null;
                if(AllinPayResultCode.isSuccess(aipgRsp))
                {
                    rep=AllinPayUtil.getTrxDataItem(aipgRsp);
                }
                PayContext.getContext().setData(rep);

                return PayContext.getContext();
            }
        });

        try {
            context=result.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }


        return context;
    }

    /**
     * 获取订单信息走常规流程
     * @param request
     * @param callable
     * @param <T>
     * @param <V>
     * @return
     */
    public <T,V> PayContext execute(final PayRequest request, final Callable<V> callable) {
        PayContext context=null;
        Future<PayContext> result = taskExecutor.submit(new Callable<PayContext>() {
            @Override
            public PayContext call() throws Exception {

                PayContext.removeContext();
                PayContext.getContext().setPayRequest(request);
                PayBehavior behavior = SpringContextUtil.getBean(PayBehaviorType.PrepareOrderBehavior);
                if (behavior != null) {
                    behavior.doInvoke();
                }

                AipgReq aipgReq = allinPayAccountMgr.createReq();
                PayContext.getContext().setAipgReq(aipgReq);
                aipgReq.getINFO().setTRX_CODE(request.getTransRequestCode());

                try {
                    aipgReq.addTrx(callable.call());
                } catch (Exception e) {
                    e.printStackTrace();
                }

                AipgRsp aipgRsp = sendReq(aipgReq);
                T rep = null;
                if (AllinPayResultCode.isSuccess(aipgRsp)) {
                    rep = AllinPayUtil.getTrxDataItem(aipgRsp);
                }

                PayContext.getContext().setData(rep);

                return PayContext.getContext();
            }
        });

        try {
            context = result.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }


        return context;
    }

    public AipgRsp sendReq(Object aipgReq)
    {
        AipgRsp aipgrsp=null;
        String reqXml = XmlTools.buildXml(aipgReq, true);
        reqXml=reqXml.replace("com.aipg.payreq.","");
        String rspXml =null;
        AllinPayConfig config=SpringContextUtil.getBean(AllinPayConfig.class);

        try {
            LogUtil.info(logger,"通联请求","开始签名");

            reqXml= AllinPayUtil.signMsg(reqXml);

            LogUtil.info(logger,"通联请求","签名成功:"+reqXml);

        } catch (Exception e) {
            e.printStackTrace();
            PayContext.getContext().setValidationResult(new ValidationResult(ValidationResultCode.VALIDATION_ERROR_SIGN));
        }

        try {

            rspXml = XmlTools.send(config.getRequestUrl(), new String(reqXml.getBytes(), "UTF-8"));
            LogUtil.info(logger,"通联请求","接受请求数据:"+rspXml);
            aipgrsp = XSUtil.parseRsp(rspXml);
        } catch (Exception e) {
            e.printStackTrace();

            PayContext.getContext().setValidationResult(new ValidationResult(ValidationResultCode.VALIDATION_ERROR_ALLINPAY_NETWORK));
        }

        if(aipgrsp!=null)
        {
            XMLParseExtension.parse(rspXml,aipgrsp);
            PayContext.getContext().setAipgRsp(aipgrsp);
        }

        sendReqRecord(reqXml,rspXml);

        return aipgrsp;
    }

    @Autowired
    private RabbitmqMessageSender rabbitmqMessageSender;

    public void sendTradeRecord(PayContext context) {

        AllinPayBillRecord record=new AllinPayBillRecord();

        BeanUtils.copyProperties(context.getPayResult(),record);

        AipgReq aipgReq= context.getAipgReq();

        if(aipgReq!=null)
        {
            record.setReqSn(aipgReq.getINFO().getREQ_SN());
        }

        AipgRsp aipgrsp=context.getAipgRsp();
        if(aipgrsp!=null)
        {
            record.setErrMsg(aipgrsp.getINFO().getERR_MSG());
            record.setRetCode(aipgrsp.getINFO().getRET_CODE());
        }

        PayRequest request=context.getPayRequest();
        if(request!=null)
        {
            record.setTrxCode(request.getTransRequestCode());
            record.setUserId(request.getUserId());
            record.setOrderNo(request.getOrderNo());
            record.setMoney(request.getMoney());

            if(request.getBankCard()!=null)
            {
                record.setMixCardNo(request.getBankCard().getMixCardNo());
            }

            AllinPayUtil.logOrderPay(logger, request, "记录交易结果");
        }
        sendStatistics(record);
    }

    public void sendReqRecord(String req,String rsp) {

        AllinPayReqRecord record=new AllinPayReqRecord();
        record.setRequestData(req);
        record.setResponseData(rsp);

        AipgReq aipgReq= PayContext.getContext().getAipgReq();

        record.setReqSn(aipgReq.getINFO().getREQ_SN());

        AipgRsp aipgrsp=PayContext.getContext().getAipgRsp();
        if(aipgrsp!=null)
        {
            record.setTrxCode(aipgReq.getINFO().getTRX_CODE());
            record.setErrMsg(aipgrsp.getINFO().getERR_MSG());
            record.setRetCode(aipgrsp.getINFO().getRET_CODE());
        }

        PayRequest request=PayContext.getContext().getPayRequest();
        if(request!=null)
        {
            record.setUserId(request.getUserId());
            record.setOrderNo(request.getOrderNo());
        }

        sendStatistics(record);
        LogUtil.info(logger, "记录通联通信记录");

    }

    private void sendStatistics(BaseRecord obj)
    {
        obj.setCreatedAt(new Date());
        StatisticsForm form=StatisticsForm.create(obj);
        rabbitmqMessageSender.send(RabbitMqQueue.RABBIT_MQ_STATISTICS, form);
    }
}
